package com.yangcangclub.api.v1.trade.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.yangcangclub.commons.annotation.*;
import com.yangcangclub.commons.exception.ApolloException;
import com.yangcangclub.commons.result.MessageEnum;
import com.yangcangclub.commons.result.ResultData;
import com.yangcangclub.commons.utils.StringUtil;
import com.yangcangclub.pay.utils.XmlUtil;
import com.yangcangclub.trade.bean.TradeVo;
import com.yangcangclub.trade.bean.UserAddress;
import com.yangcangclub.trade.service.TradeService;
import com.yangcangclub.trade.service.UserAddressService;
import com.yangcangclub.user.component.UserComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/yangcangclub/v1/trade")
public class TradeController {
    private static Logger logger = LoggerFactory.getLogger(TradeController.class);

    @Autowired
    TradeService tradeService;
    @Autowired
    UserAddressService userAddressService;
    @Autowired
    UserComponent userComponent;
    @Autowired
    public RedisTemplate<Object,Object> redisTemplate;

    /** 生成订单
     * @param user
     * @param totalPrice
     * @param productList
     * @param payType
     * @param source
     * @param userRemark
     * @param addressId
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/buy", method = { RequestMethod.POST })
    @ApolloSecurity(auth = AuthEnum.USER,limit = 50,time = TimeEnum.MINUTE)
    public ResultData buy(@GetUser User user, String totalPrice, String productList,String uuid,
                          String payType, String source/*, String province,
                          String city, String county, String contactName,
                          String contactMobile, String contactAddress,int expressId*/,
                          @RequestParam(required=false,defaultValue = "")String userRemark,
                          int addressId){
        JSONObject obj=JSON.parseObject(productList);
        ResultData resultData ;
        try {
            //校验
            if(!isDoTradeFirst(uuid)){
                return new ResultData(MessageEnum.REPEAT_OPERATION);
            }
            UserAddress userAddress = userAddressService.getUserAddressDetails(addressId);
            if(userAddress==null){
                throw new ApolloException(MessageEnum.ERROR_ADDRESS);
            }
            String province = userAddress.getProvince();
            String city = userAddress.getCity();
            String county = userAddress.getCounty();
            String contactName = userAddress.getContactName();
            String contactMobile = userAddress.getContactMobile();
            String contactAddress = userAddress.getAddress()+"$%$"+userAddress.getContactAddress();
            Map<String,Object> map = tradeService.buy(totalPrice,user.getId(),obj, payType, source,
                    province, city, county, contactName, contactMobile, contactAddress,
            -1, userRemark, addressId);
            if(map.isEmpty()){
                resultData = new ResultData(MessageEnum.FAILURE);
            }
            resultData = new ResultData(MessageEnum.SUCCESS,map);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**获取订单列表
     * @param user
     * @param orderBy
     * @param searchInfo
     * @param pageNum
     * @param pageSize
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/showMyList")
    @ApolloSecurity(auth = AuthEnum.USER,limit = 50,time = TimeEnum.MINUTE)
    public ResultData showMyList(@GetUser User user,
                                 @RequestParam(required=false,defaultValue = "dateDown")String orderBy,
                                 @RequestParam(required=false,defaultValue = "")String searchInfo,
                                 @RequestParam(required=false,defaultValue = "1")int pageNum,
                                 @RequestParam(required=false,defaultValue = "10")int pageSize
                                 ){
        ResultData resultData ;
        try {
            if(StringUtil.isEmpty(orderBy)){
                orderBy="dateDown";
            }
            //业务
            if("dateDown".equals(orderBy)){
                orderBy = "create_date desc";
            }
            if("dateUp".equals(orderBy)){
                orderBy = "create_date asc";
            }
            Map map = Maps.newHashMap();
            map.put("userId",user.getId());
            if(StringUtil.isNotEmpty(searchInfo)){

                JSONObject obj=JSON.parseObject(searchInfo);
                if(obj.getString("status").indexOf(",")>-1){
                    String[] statusStr = obj.getString("status").trim().split(",");
                    List<String> statusInList = new ArrayList<>();
                    for(String st:statusStr){
                        statusInList.add(st);
                    }
                    map.put("statusInList",statusInList);
                }else{
                    map.put("status",obj.containsKey("status")?obj.getString("status"):"");
                }
//            map.put("expressNo",searchInfo.containsKey("expressNo")?searchInfo.getString("expressNo"):"");
//            map.put("contactMobile",searchInfo.containsKey("contactMobile")?searchInfo.getString("contactMobile"):"");
//            map.put("tradeNo",searchInfo.containsKey("tradeNo")?searchInfo.getString("tradeNo"):"");
//            map.put("payType",searchInfo.containsKey("payType")?searchInfo.getString("payType"):"");
//            map.put("transactionNo",searchInfo.containsKey("transactionNo")?searchInfo.getString("transactionNo"):"");
                map.put("payStatus",obj.containsKey("payStatus")?obj.getString("payStatus"):"");
//            map.put("source",searchInfo.containsKey("source")?searchInfo.getString("source"):"");
//            map.put("contactAddress",searchInfo.containsKey("contactAddress")?searchInfo.getString("contactAddress"):"");

                map.put("createDateBegin",obj.containsKey("createDateBegin")?obj.getString("createDateBegin"):"");
                map.put("createDateEnd",obj.containsKey("createDateEnd")?obj.getString("createDateEnd"):"");
            }

            Map<String,Object> tradeMap = tradeService.getTradeList(pageNum,pageSize,orderBy,map);
            resultData = new ResultData(MessageEnum.SUCCESS,tradeMap);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**后台管理 获取订单列表
     * @param user
     * @param offset
     * @param limit
     * @param tradeNo
     * @param contactMobile
     * @param createDateBegin
     * @param createDateEnd
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/manager/showTradeList")
    @ApolloSecurity(auth = AuthEnum.ADMIN,limit = 50,time = TimeEnum.MINUTE)
    public ResultData managerShowTradeList(@GetUser User user,
                                           @RequestParam(required=false,defaultValue = "0")String offset,
                                           @RequestParam(required=false,defaultValue = "10")String limit,
                                           @RequestParam(required=false,defaultValue = "")String tradeNo,
                                           @RequestParam(required=false,defaultValue = "")String contactMobile,
                                           @RequestParam(required=false,defaultValue = "")String createDateBegin,
                                           @RequestParam(required=false,defaultValue = "")String createDateEnd,
                                           @RequestParam(required=false,defaultValue = "")String status,
                                           @RequestParam(required=false,defaultValue = "")String contactName){
        ResultData resultData ;
        try {
            Map<String,Object> map = Maps.newHashMap();
            //业务
            map.put("tradeNo",StringUtil.isNotEmpty(tradeNo)?tradeNo:"");
            map.put("contactMobile",StringUtil.isNotEmpty(contactMobile)?contactMobile:"");
            map.put("contactName",StringUtil.isNotEmpty(contactName)?contactName:"");
            if(status.indexOf(",")>-1){
                String[] statusStr = status.trim().split(",");
                List<String> statusInList = new ArrayList<>();
                for(String st:statusStr){
                    statusInList.add(st);
                }
                map.put("statusInList",statusInList);
            }else{
                map.put("status",StringUtil.isNotEmpty(status)?status:"");
            }
            if(StringUtil.isNotEmpty(createDateBegin)){
                map.put("createDateBegin",StringUtil.checkDateFormat(createDateBegin)?createDateBegin:"");
            }
            if(StringUtil.isNotEmpty(createDateEnd)){
                map.put("createDateEnd",StringUtil.checkDateFormat(createDateEnd)?createDateEnd:"");
            }
            map.put("sortColumns","create_date desc");
            Map<String, Object> mapResult = tradeService.getManagerTradeList(Integer.valueOf(offset),Integer.valueOf(limit),map);
            resultData = new ResultData(MessageEnum.SUCCESS,mapResult);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**获取订单详细
     * @param user
     * @param tradeId
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/showTrade")
    @ApolloSecurity(auth = AuthEnum.USER,limit = 50,time = TimeEnum.MINUTE)
    public ResultData showTrade(@GetUser User user,int tradeId){
        ResultData resultData ;
        try {
            //判断
            if(tradeId<1){
                throw new ApolloException(MessageEnum.PARMA_FORMAT_ERROR);
            }

            TradeVo tradeVo = tradeService.getTradeDetailsById(user.getId(),tradeId);
            resultData = new ResultData(MessageEnum.SUCCESS,tradeVo);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**后台管理 获取订单详细
     * @param user
     * @param tradeId
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/manager/showTrade")
    @ApolloSecurity(auth = AuthEnum.ADMIN,limit = 50,time = TimeEnum.MINUTE)
    public ResultData managerShowTrade(@GetUser User user,
                                       @RequestParam(required=false,defaultValue = "0")int tradeId,
                                       @RequestParam(required=false,defaultValue = "")String tradeNo){
        ResultData resultData ;
        try {
            //判断
            if(tradeId<1 && StringUtil.isEmpty(tradeNo)){
                throw new ApolloException(MessageEnum.PARMA_FORMAT_ERROR);
            }
            Map<String,Object> map = Maps.newHashMap();
            TradeVo tradeVo = null;
            if(StringUtil.isEmpty(tradeNo)){
                tradeVo = tradeService.getTradeDetailsById(tradeId);
            }else{
                tradeVo = tradeService.getTradeDetailsByTradeNo(tradeNo);
            }
            Map<String,Object> u = userComponent.getUserInfo(tradeVo.getTradeSummary().getUserId());
            map.put("trade",tradeVo);
            map.put("user",u);
            resultData = new ResultData(MessageEnum.SUCCESS,map);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**支付宝支付异步通知
     * @param request
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/alipay/notify")
    @ApolloSecurity(auth = AuthEnum.PUBLIC,limit = 50,time = TimeEnum.MINUTE)
    public ResultData alipayNotify(HttpServletRequest request, HttpServletResponse response){
        logger.info("支付宝异步通知接收消息");
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            //			params.put(name, URLDecoder.decode(valueStr, "UTF-8") );
            params.put(name, valueStr);
        }
        logger.info("支付宝异步通知参数:"+params.toString());

        try {
            logger.info("支付宝异步通知参数验证");
            boolean b = tradeService.tradeAlipayNotify(params);
            logger.info("支付宝异步通知参数验证结果"+b);
            PrintWriter pw = response.getWriter();
            if(b){
                pw.write("success");
            }else
                pw.write("failure");
            pw.flush();
        } catch (IOException e) {
            logger.error("支付宝异步回调错误："+params.toString());
        }
        return null;
    }

    /**微信支付异步通知
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "/wechat/notify")
    @ApolloSecurity(auth = AuthEnum.PUBLIC,limit = 50,time = TimeEnum.MINUTE)
    public ResultData wechatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
        logger.info("微信支付异步通知接收消息");
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        String result  = new String(outSteam.toByteArray(),"utf-8");
        Map<String, String> params = XmlUtil.doXMLParse(result);

        logger.info("微信异步通知参数:"+params.toString());

        try {
            logger.info("微信异步通知参数验证");
            boolean b = tradeService.tradeWechatNotify(params);
            logger.info("微信异步通知参数验证结果"+b);
            PrintWriter pw = response.getWriter();
            if(b){
                response.getWriter().write(XmlUtil.setXML("SUCCESS", "OK"));
            }
            pw.flush();
        } catch (IOException e) {
            logger.error("微信异步回调错误："+params.toString());
        }
        return null;
    }

    /**修改订单状态（已完成、已取消）
     * @param user
     * @param status
     * @param tradeId
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/updateTradeStatus", method = { RequestMethod.POST })
    @ApolloSecurity(auth = AuthEnum.USER,limit = 50,time = TimeEnum.MINUTE)
    public ResultData updateTradeStatus(@GetUser User user,String status,int tradeId ){
        ResultData resultData ;
        try {
            if(tradeId <= 0){
                throw new ApolloException(MessageEnum.PARMA_FORMAT_ERROR);
            }
            boolean b = tradeService.updateTradeStatus(user.getId(),tradeId,status);
            if(b){
                resultData = new ResultData(MessageEnum.SUCCESS);
            }else{
                resultData = new ResultData(MessageEnum.FAILURE);
            }
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**后台管理修改订单状态（已完成、已取消）
     * @param user
     * @param status
     * @param serviceRemark
     * @param tradeId
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/manager/updateTradeStatus", method = { RequestMethod.POST })
    @ApolloSecurity(auth = AuthEnum.ADMIN,limit = 50,time = TimeEnum.MINUTE)
    public ResultData managerUpdateTradeStatus(@GetUser User user, String status,
                                               @RequestParam(value="serviceRemark",defaultValue = "")String serviceRemark,
                                        int tradeId ){
        ResultData resultData ;
        try {
            if(tradeId <= 0){
                throw new ApolloException(MessageEnum.PARMA_FORMAT_ERROR);
            }
            boolean b = tradeService.updateTradeStatus(tradeId,status,serviceRemark,user.getId());
            if(b){
                resultData = new ResultData(MessageEnum.SUCCESS);
            }else{
                resultData = new ResultData(MessageEnum.FAILURE);
            }
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**后台管理更新订单信息（物流信息、配送地址信息、客服备注）
     * @param user
     * @param updateType
     * @param tradeId
     * @param expressName
     * @param expressId
     * @param expressNo
     * @param province
     * @param city
     * @param county
     * @param contactName
     * @param contactMobile
     * @param contactAddress
     * @param serviceRemark
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/manager/updateTrade", method = { RequestMethod.POST })
    @ApolloSecurity(auth = AuthEnum.ADMIN,limit = 50,time = TimeEnum.MINUTE)
    public ResultData managerUpdateTrade(@GetUser User user, String updateType,int tradeId,
                                         @RequestParam(required=false,defaultValue = "")String expressName,
                                         @RequestParam(required=false,defaultValue = "0")int expressId,
                                         @RequestParam(required=false,defaultValue = "")String expressNo,
                                         @RequestParam(required=false,defaultValue = "")String province,
                                         @RequestParam(required=false,defaultValue = "")String city,
                                         @RequestParam(required=false,defaultValue = "")String county,
                                         @RequestParam(required=false,defaultValue = "")String contactName,
                                         @RequestParam(required=false,defaultValue = "")String contactMobile,
                                         @RequestParam(required=false,defaultValue = "")String contactAddress,
                                         @RequestParam(required=false,defaultValue = "")String serviceRemark){
        ResultData resultData ;
        try {
            JSONObject updateInfo = new JSONObject();
            updateInfo.put("id",tradeId);
            //更新物流信息
            if("express".equals(updateType)){
                updateInfo.put("expressName",expressName);
                updateInfo.put("expressId",expressId);
                updateInfo.put("expressNo",expressNo);
                updateInfo.put("status","R");//变更为已发货状态
                updateInfo.put("serviceRemark",serviceRemark);
            }
            //更新地址信息
            if("contact".equals(updateType)) {
                updateInfo.put("province", province);
                updateInfo.put("city", city);
                updateInfo.put("county", county);
                updateInfo.put("contactName", contactName);
                updateInfo.put("contactMobile", contactMobile);
                updateInfo.put("contactAddress", contactAddress);
                updateInfo.put("serviceRemark",serviceRemark);
            }
            if("other".equals(updateType)){
                updateInfo.put("serviceRemark",serviceRemark);
            }
            boolean b = tradeService.updateTrade(updateType,tradeId,updateInfo,user.getId());
            if(b){
                resultData = new ResultData(MessageEnum.SUCCESS);
            }else{
                resultData = new ResultData(MessageEnum.FAILURE);
            }
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    /**继续支付-获取支付参数
     * @param user
     * @param tradeId
     * @param payType
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/continuePay", method = { RequestMethod.POST })
    @ApolloSecurity(auth = AuthEnum.USER,limit = 50,time = TimeEnum.MINUTE)
    public ResultData continuePay(@GetUser User user,int tradeId,String payType){
        ResultData resultData ;
        try {

            Map<String,Object> map = tradeService.continuePay(user.getId(),tradeId, payType);
            if(map.isEmpty()){
                resultData = new ResultData(MessageEnum.FAILURE);
            }
            resultData = new ResultData(MessageEnum.SUCCESS,map);
        }  catch (ApolloException apoEx) {
            resultData=new ResultData(apoEx);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            resultData=new ResultData(e);
        }
        return resultData;
    }

    public boolean isDoTradeFirst(String token){
        if(StringUtil.isAllFullEmpty(token)){throw new ApolloException(MessageEnum.PARMA_RANGE_ERROR);}
        BoundValueOperations bvo = redisTemplate.boundValueOps(token);
        int expireSeconds=10;//10秒token过期
        //判断是否存在token
        if( bvo.setIfAbsent(1)) {
            bvo.expire(expireSeconds, TimeUnit.SECONDS);
            return true;
        }else{
            return false;
        }
    }
}
